Skip to content

feat: per-request observability (method, latency, errors) - #12

Merged
klaidliadon merged 1 commit into
masterfrom
feat/request-observability
Jun 20, 2026
Merged

klaidliadon merged 1 commit into
masterfrom
feat/request-observability

Conversation

@klaidliadon

Copy link
Copy Markdown
Collaborator

The server had no per-request observability — only a startup line plus error/panic logs — so there was no way to see which LSP methods are called or how long they take. This adds request-level logging.


Change

ObserveHandler — a jsonrpc2 middleware (mirroring RecoverHandler) wrapping the server handler. For every request it logs:

  • method (e.g. textDocument/completion)
  • duration_ms — request body → reply, i.e. server processing time (not transport or queue wait; handlers are serialized by AsyncHandler)
  • error — only when the request failed

It logs at Debug, so it's silent unless RIDL_LSP_LOG_LEVEL=debug, and writes only to the server logger (stderr → the editor's output channel). stdout is the JSON-RPC transport and is never logged to. Placed outermost (ObserveHandler(RecoverHandler(ServerHandler))) so even recovered-panic requests are timed and logged with their error.

Usage

Set RIDL_LSP_LOG_LEVEL=debug in the server's environment. Every request then logs a structured line, e.g. {"method":"textDocument/hover","duration_ms":1.83}.

Test plan

$ go test -race ./...
ok  ./...

$ golangci-lint run ./... -c .golangci.yml
0 issues.

New tests use zap's observer to assert the logged fields: method + duration_ms present on success (no error field), an error field on a failed request, and reply forwarding intact.

Not included (deliberate)

Aggregation (per-method p50/p95) and metrics export (Prometheus/OTel) were scoped out — this is the per-request log layer. Grep duration_ms for latency; aggregation can follow if needed.

The server had no per-request observability — only a startup line and error/panic logs — so there was no way to see which LSP methods are called or how long they take.

Add ObserveHandler, a jsonrpc2 middleware (mirroring RecoverHandler) wrapping the server handler. It logs method, duration_ms (request body to reply — server processing time), and any error for every request, at Debug level so it is silent unless RIDL_LSP_LOG_LEVEL=debug. Output goes to the server logger (stderr / the editor's output channel), never stdout. Placed outermost so even recovered-panic requests are timed and logged.
@klaidliadon
klaidliadon merged commit 33ae852 into master Jun 20, 2026
1 check passed
@klaidliadon
klaidliadon deleted the feat/request-observability branch July 2, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant